Skip to content

Аудит и исправление расчётов плагина ton-trading-bot (#182)#183

Open
konard wants to merge 10 commits into
xlabtg:mainfrom
konard:issue-182-cd0a047fa72e
Open

Аудит и исправление расчётов плагина ton-trading-bot (#182)#183
konard wants to merge 10 commits into
xlabtg:mainfrom
konard:issue-182-cd0a047fa72e

Conversation

@konard

@konard konard commented Jun 13, 2026

Copy link
Copy Markdown

Что и почему

Issue #182 сообщает о критических ошибках в расчётах плагина ton-trading-bot и просит провести полный аудит и привести плагин в состояние, готовое к реальным транзакциям. Корневая причина почти всех багов — смешивание единиц измерения (TON, USDT, USD, разные джеттоны складывались/вычитались напрямую).

Каждое исправление сопровождается воспроизводящим тестом, который падает на старом коде и проходит на новом.


Соответствие пунктам issue

🚨 1. Критический баг: смешение единиц в P&L

Было: pnl = exit_quote_amount − entry_base_amount (вычитали 10 TON из 17.4 USDT → фейковые +$7.39 / +73.89 % на плоском трейде).

Стало (closeTradeJournalEntry, index.js): P&L считается из двух величин в одной единице:

  • если известны USD-цены входа и выхода базового актива: pnl = base_amount × (exit_price − entry_price);
  • иначе — fallback на разницу в одной валюте (amount_out − amount_in), только когда обе ноги в одной единице.

Дополнительно исправлено начисление на симуляционный баланс: раньше на TON-баланс зачислялась сумма выхода в USDT (отсюда «доказательство из баланса» 1000 → 1004.25 в отчёте). Теперь баланс остаётся в TON: amount_in + pnl / exit_ton_price_usd. Цена входа/выхода резолвится автоматически (inferAssetPriceUsd), даже если агент её не передал, — это устраняет первопричину.

📊 3. Статистические нестыковки

total_closed_tradeswin + loss, и «65 поражений при average loss $0». Введён единый классификатор закрытых сделок (classifyClosedTrades): сделка считается выигрышной/проигрышной/безубыточной строго по знаку реализованного pnl, а сделки без посчитанного P&L помечаются как unscored. Теперь во всех инструментах статистики выполняется тождество total_closed = wins + losses + breakeven + unscored.

⏱ 4. Зависание DCA-ордеров (0 из 30 исполнено)

Корневая причина — отсутствие атомарного исполнения: ордер не «забирался» перед свопом. Добавлен ton_trading_execute_scheduled_trade — он помечает due-ордер executed через compare-and-swap (CAS) до свопа, поэтому ордер не может исполниться дважды и не зависает в pending. Инструмент dm-only.

📉 5. Проскальзывание при редком мониторинге

По природе планировщик опрашивает цену периодически, поэтому выход возможен не точно на уровне TP. Закрытие теперь фиксирует фактическую цену выхода в exit_price_usd, а реализованный P&L считается от неё — отчёт перестаёт выдавать желаемое за действительное.

Пункт 2 (галлюцинации агента в объяснении математики) — это поведение промпта агента, а не плагина; устраняется тем, что плагин больше не выдаёт некорректную формулу, вокруг которой агент строил ложную логику.


Полный аудит (сверх пунктов issue)

  • Порядок свечей OHLCV. Индикаторы считались на данных в обратном порядке. Свечи нормализуются по возрастанию времени перед расчётом RSI/MACD/Bollinger.
  • Математика индикаторов и риск-метрик. Выверены RSI, MACD, полосы Боллинджера, VaR, max drawdown и Sharpe.
  • Безопасность реальных средств.
    • Закрытие позиций (close_position / close_all_positions) защищено CAS от двойного исполнения.
    • auto_execute переведён на единый безопасный путь реального свопа (recordRealSwap: проверка инициализации кошелька, журналирование после ончейна, CRITICAL-лог при сбое журнала).
    • Все инструменты, тратящие реальные средства, помечены scope: "dm-only" (execute_swap, auto_execute, execute_scheduled_trade, close_position, close_all_positions).
  • Честные unit-safe метрики.
    • portfolio_summary: понозиционный нереализованный P&L в USD и полная экспозиция в TON (не-TON ноги мостятся через USD); поля exposure_complete, valued/unvalued_open_positions.
    • get_top_traders: убран бессмысленный «win rate» из сравнения цен двух разных токенов; ранжирование по наблюдаемому объёму в USD и числу сделок.
    • get_trader_performance: вместо сравнения двух разных джеттонов — учёт потока TON (net_ton_flow) с честной пометкой, что это не реализованная прибыль.
    • optimal_position_size: fixed-fraction размер ограничен балансом (спот без плеча не может превышать кошелёк); флаг fixed_fraction_capped_by_balance.
    • order_book_depth: поле bid_ask_spread переименовано в price_spread_across_sizes — котировки односторонние, это не настоящий bid/ask-спред.
  • Документация (README/manifest). Список инструментов приведён к 42/42 (был пропущен execute_scheduled_trade), убраны устаревшие формулировки («ranked by win rate»), описания приведены в соответствие с кодом. Версия в manifest.json: 2.2.0 → 2.4.0.

Тестирование

  • Перед каждым исправлением добавлен тест, воспроизводящий баг (проверено: падает на старом коде, проходит на новом).
  • Полный набор тестов плагина: 188/188 проходят.
  • Локальный CI: eslint — 0 ошибок (остаются только унаследованные предупреждения о неиспользуемых переменных); tsc --noEmit — без ошибок; validate-pluginston-trading-bot: 42 tool(s) validated.

Примечание: в полном прогоне по всем плагинам падает один тест vk-full-admin (Cannot find module 'vk-io') — это унаследованная проблема отсутствующей опциональной зависимости в другом плагине, не затронутом этим PR.

Closes #182

konard added 9 commits June 13, 2026 17:16
Adding .gitkeep for PR creation (default mode).
This file will be removed when the task is complete.

Issue: xlabtg#182
Корневая причина: при открытии сделки entry_price_usd был опциональным.
Когда агент его не указывал, но цена выхода присутствовала, closeTradeJournalEntry
сравнивал сырое количество TON (как будто это USD) со стоимостью в USD,
давая бессмысленные +$7.39 / +73.89% на сделке TON/USDT с почти неизменной ценой.

- closeTradeJournalEntry: P&L считается только из величин в ОДНОЙ единице.
  Ценовой режим требует обе цены (вход+выход): pnl = base * (exit - entry);
  иначе безопасный fallback на сырую разницу в одной единице.
- simulate_trade/execute_swap: автозапись цены входа from_asset (TON через
  getPrice, стейблкоины = $1), чтобы закрытие всегда было unit-safe.
- record_trade: авто-вывод цены выхода (как в close_position).
- auto_execute: цена входа берётся для from_asset, а не всегда TON.
- Тесты: воспроизведение сценария xlabtg#182 (open без entry_price -> close)
  теперь даёт ~$0.58 / 3.45% вместо $7.39 / 73.89%.
Расхождения из отчёта: total_closed_trades=177 при win+loss=172 и
"65 убытков, но средний убыток $0". Корень — сделки с pnl==0 (breakeven)
и pnl==null (P&L не записан) молча приравнивались к 0: не попадали ни в
win, ни в loss, но раздували знаменатель win_rate и средние значения.

- Добавлен общий классификатор summarizeClosedPnl: каждая закрытая сделка
  попадает ровно в одну корзину, инвариант win+loss+breakeven+unscored=total.
- get_portfolio_summary и get_performance_dashboard: новые поля
  breakeven_count/unscored_count; win_rate считается по решающим сделкам
  (win+loss); средние и realized P&L — только по сделкам с конечным pnl.
- calculate_risk_metrics: несписанные сделки (null pnl_percent) исключены
  из ряда наблюдений (не искажают VaR/просадку/Sharpe), добавлено
  scored_trades и win/loss/breakeven счётчики.
- Тесты: воспроизведение расхождения 177/172 и проверка, что несписанные
  сделки не разбавляют win_rate и не дают ложный средний убыток $0.
…х сделок

Корень проблемы (issue xlabtg#182): ни один инструмент не переводил
запланированную сделку из статуса 'pending' в 'executed'. Из-за этого
исполненный due-ордер навсегда оставался 'pending', снова попадал в
выборку due и мог быть исполнен повторно — двойная трата реальных средств.

Изменения:
- Новый инструмент ton_trading_execute_scheduled_trade (dm-only): атомарно
  захватывает ордер через compare-and-swap (UPDATE ... WHERE status='pending'),
  затем выполняет своп. Повторный вызов по тому же id — no-op.
- Семантика отказа, безопасная для реальных средств: исключение реального
  свопа после захвата → терминальный статус 'failed' (состояние в сети
  неизвестно, без авто-ретрая); исключение симуляции или провал валидации
  до свопа → возврат в 'pending' (безопасный ретрай).
- Общие хелперы recordSimulatedSwap/recordRealSwap — единый источник логики
  свопа и журналирования для simulate_trade, execute_swap и нового инструмента.
- Миграция: колонка trade_id в scheduled_trades связывает ордер с журналом.
- Статус-схема scheduled_trades дополнена 'failed'.
- Описания schedule_trade/get_scheduled_trades/create_schedule направляют LLM
  к последовательности get → execute (вместо прямого swap/simulate).
- Версия 2.2.0 → 2.3.0 (index.js + manifest.json), запись инструмента в манифест.
- 7 новых тестов, включая защиту от двойного исполнения и парковку failed-ордера.
Аудит выявил несколько ошибок в расчётах, искажавших сигналы и метрики:

- OHLCV от GeckoTerminal приходит newest-first (по убыванию timestamp).
  Код брал closes[last] как текущую цену, получая самую СТАРУЮ свечу, а
  RSI/MACD считались по перевёрнутому времени. Добавлена сортировка по
  возрастанию timestamp в get_technical_indicators.

- Сигнальная линия MACD считалась как 9-периодная EMA сырых цен, а не самой
  линии MACD. Введён emaSeries(): signal = 9-EMA ряда MACD (его определение),
  гистограмма теперь корректна.

- Sharpe ratio использовал популяционную дисперсию (делитель n). Заменено на
  выборочное стандартное отклонение (n-1) в calculate_risk_metrics и backtest;
  добавлен guard на >= 2 наблюдения, чтобы не делить на ноль.

- VaR применял Math.abs() к процентилю доходности, превращая выигрышный
  персентиль в фантомный «риск». Теперь value_at_risk = max(0, -var95):
  ноль, когда исторических убытков на этом уровне доверия нет.

- Исправлен неверный комментарий формулы Kelly (код был верным).

Тесты: порядок свечей (current_price = новейшая), сигнальная линия MACD,
Sharpe (n-1) в risk-metrics и backtest, нулевой VaR для прибыльной истории.
issue xlabtg#182: closeOpenPosition и closeTradeJournalEntry не были защищены
от гонок и повторов, из-за чего одну позицию можно было закрыть дважды —
дважды отправить обратный своп (реальные средства) или дважды начислить
sim-баланс.

- closeOpenPosition: атомарный захват 'open' → 'closing' перед любым
  движением средств; победитель CAS продолжает, остальные получают отказ.
  Симуляция при ошибке/пустом выводе освобождает захват ('closing' → 'open')
  для безопасного повтора; реальный своп при исключении или неизвестном
  выводе переводит сделку в терминальный 'close_failed' (ручная сверка),
  чтобы авто-повтор не потратил средства повторно.
- closeTradeJournalEntry: закрытие через CAS (WHERE status != 'closed'),
  чтобы два конкурентных record_trade/close не начислили sim-баланс дважды.
- Схема trade_journal.status: добавлены 'closing' и 'close_failed'.
- Тесты: воспроизведение гонки реального закрытия (своп ровно один раз) и
  гонки record_trade (sim-баланс начисляется один раз); мок makeStatefulDb
  честно моделирует CAS-переходы trade_journal; инлайн-моки возвращают changes.

Тесты: 180/180. Lint/typecheck/validate чисто.
Реальный режим auto_execute больше не обходит проверки для не-TON активов
и не дублирует своп/INSERT в обход общих хелперов.

- Валидация была привязана к from_asset === "TON": реальные продажи
  джеттонов пропускали проверку кошелька, баланса и maxTradePercent, а
  своп вызывался напрямую. Теперь путь идёт через recordRealSwap /
  recordSimulatedSwap — те же гарантии, что у execute_swap и
  execute_scheduled_trade (проверка инициализации кошелька, безопасный
  вывод entry price, контракт «бросать исключение при сбое после свопа»).
- Риск-лимит (%% от баланса и минимальный резерв) унифицирован: считается
  в TON, поэтому применяется к продажам TON в обоих режимах; в реальном
  режиме добавлена недостающая проверка резерва после сделки.
- recordRealSwap: если своп прошёл on-chain, но запись в журнал упала,
  пишется громкий CRITICAL-лог (средства ушли без записи — нужна ручная
  сверка) и исключение пробрасывается дальше.

Тесты (воспроизводят баги, падают на старом коде):
- реальная не-TON продажа без кошелька больше не вызывает swap;
- happy-path реальной не-TON продажи проходит через recordRealSwap;
- сбой записи журнала после реального свопа логируется как CRITICAL.
portfolio_summary теперь реально считает нереализованный P&L, который
обещало описание: по каждой открытой позиции стоимость и база считаются
в одной единице (USD) через цены TON/стейблкоинов, позиции без известной
цены помечаются как unvalued, а не смешиваются. total_exposure_ton больше
не суммирует только TON-фундированные ноги — экспозиция остальных мостится
через USD-базу и цену TON, с флагом exposure_complete честности покрытия.

get_top_traders: убрана выдуманная win_rate, которая вычиталась из цен двух
РАЗНЫХ токенов пула (класс бага xlabtg#182) и фильтровала активные кошельки.
Теперь ранжирование по объёму в USD и количеству сделок (одна единица),
с note, что прибыльность по снимку сделок не определить.

get_trader_performance: вместо сравнения amount_in/amount_out двух разных
джеттонов (1 TON → 5 000 000 мемкоина всегда «выигрыш») считается поток TON
в единых нанотонах: ton_spent/ton_received/net_ton_flow и buys/sells.

Тесты-репродукции для всех трёх инструментов, каждый проверен на падение
на старом коде. Итого 186/186.
optimal_position_size: ограничить fixed-fraction размер балансом — спотовая
торговля на TON без плеча, поэтому позиция не может превышать кошелёк
(risk 2% / stop 1% давало 200% баланса). Добавлен флаг
fixed_fraction_capped_by_balance.

order_book_depth: переименовать вводящее в заблуждение поле bid_ask_spread в
price_spread_across_sizes — котировки односторонние (from→to), поэтому это не
настоящий bid/ask-спред, а разброс цены по размерам заявки. Описание уточнено.

README: добавлен пропущенный инструмент execute_scheduled_trade в список (42/42),
в таблицу и в перечень dm-only; убрано устаревшее «ranked by win rate» у
get_top_traders; описание order_book_depth приведено в соответствие.

manifest: версия 2.3.0 → 2.4.0, описание order_book_depth уточнено.

Каждое исправление сопровождается воспроизводящим тестом, который падает на
старом коде. Полный прогон: 188/188 для ton-trading-bot.
@konard konard changed the title [WIP] Plugin ton trading bot checking for bugs in calculations and in general Аудит и исправление расчётов плагина ton-trading-bot (#182) Jun 13, 2026
@konard konard marked this pull request as ready for review June 13, 2026 19:17
@konard

konard commented Jun 13, 2026

Copy link
Copy Markdown
Author

Working session summary

Всё завершено. Итоговый отчёт по issue #182.

Что сделано

Корневая причина почти всех багов — смешивание единиц измерения (TON, USDT, USD, разные джеттоны вычитались/складывались напрямую). Исправлено по всем пунктам issue плюс полный аудит.

По пунктам issue:

  • 🚨 Unit mismatch в P&Lpnl = base_amount × (exit_price − entry_price) вместо вычитания TON из USDT. Начисление на симбаланс теперь остаётся в TON (amount_in + pnl/exit_ton_price), а не зачисляет сумму выхода в USDT (та самая аномалия 1000 → 1004.25).
  • 📊 Статистика — единый классификатор: total_closed = wins + losses + breakeven + unscored; «65 поражений при average loss $0» больше невозможно.
  • ⏱ DCA-ордераexecute_scheduled_trade с compare-and-swap: ордер не зависает в pending и не исполняется дважды.
  • 📉 Проскальзывание — фиксируется фактическая цена выхода, P&L считается от неё.

Аудит сверх issue: порядок свечей OHLCV, математика RSI/MACD/Bollinger/VaR/Sharpe, защита закрытия позиций (CAS), безопасный путь auto_execute, dm-only на всех тратящих реальные средства инструментах, честные метрики (portfolio_summary, get_top_traders, get_trader_performance, optimal_position_size, order_book_depth), документация 42/42 и версия 2.2.0 → 2.4.0.

Верификация

  • Каждое исправление подкреплено воспроизводящим тестом (проверено: падает на старом коде, проходит на новом).
  • 188/188 тестов плагина проходят; eslint — 0 ошибок; tsc --noEmit — чисто; validate-plugins — 42 инструмента.
  • CI на HEAD-коммите 6ffa4acзелёный (CI, CodeQL, Deploy to Vercel).
  • Рабочее дерево чистое, ветка синхронизирована с main.

PR

#183 готов к ревью (снят статус draft): #183

Заголовок и описание обновлены на русском, с разбивкой по каждому пункту issue. Все коммиты в ветке issue-182-cd0a047fa72e, история сохранена.

Единственный «красный» в локальном полном прогоне — vk-full-admin (Cannot find module 'vk-io'): унаследованная нехватка опциональной зависимости в другом плагине, не затронутом этим PR. В CI он зелёный, т.к. там зависимости установлены.


This summary was automatically extracted from the AI working session output.

@konard

konard commented Jun 13, 2026

Copy link
Copy Markdown
Author

🤖 Solution Draft Log

This log file contains the complete execution trace of the AI solution draft process.

💰 Cost estimation:

  • Public pricing estimate: $40.644872
  • Calculated by Anthropic: $44.480664
  • Difference: $3.835793 (+9.44%)

📊 Context and tokens usage:

Claude Opus 4.8: (10 sub-sessions)

  1. 115.7K / 1M (12%) input tokens, 38.1K / 128K (30%) output tokens
  2. 108.8K / 1M (11%) input tokens, 51.7K / 128K (40%) output tokens
  3. 115.2K / 1M (12%) input tokens, 40.5K / 128K (32%) output tokens
  4. 116.9K / 1M (12%) input tokens, 49.2K / 128K (38%) output tokens
  5. 115.1K / 1M (12%) input tokens, 34.1K / 128K (27%) output tokens
  6. 110.3K / 1M (11%) input tokens, 46.9K / 128K (37%) output tokens
  7. 115.6K / 1M (12%) input tokens, 37.9K / 128K (30%) output tokens
  8. 115.5K / 1M (12%) input tokens, 51.9K / 128K (41%) output tokens
  9. 116.1K / 1M (12%) input tokens, 51.0K / 128K (40%) output tokens
  10. 96.5K / 1M (10%) input tokens, 30.4K / 128K (24%) output tokens

Total: (108.2K new + 1.3M cache writes + 35.0M cache reads) input tokens, 568.8K output tokens, $40.617133 cost

Claude Haiku 4.5:

  • 14.4K / 200K (7%) input tokens, 674 / 64K (1%) output tokens

Total: 14.4K input tokens, 674 output tokens, $0.027739 cost

🤖 Models used:

  • Tool: Anthropic Claude Code
  • Requested: opus
  • Main model: Claude Opus 4.8 (claude-opus-4-8)
  • Additional models:
    • Claude Haiku 4.5 (claude-haiku-4-5-20251001)

📎 Log file uploaded as Repository (32435KB)


Now working session is ended, feel free to review and add any feedback on the solution draft.

@konard

konard commented Jun 13, 2026

Copy link
Copy Markdown
Author

✅ Ready to merge

This pull request is now ready to be merged:

  • All CI checks have passed
  • No merge conflicts
  • No pending changes

Monitored by hive-mind with --auto-restart-until-mergeable flag

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Plugin ton trading bot checking for bugs in calculations and in general

1 participant